package com.elitesland.security.config;

import com.elitesland.core.exception.BusinessException;
import com.elitesland.org.service.EmployeeService;
import com.elitesland.security.DynamicFilterInvocationSecurityMetadataSource;
import com.elitesland.security.RequestMatcherCreator;
import com.elitesland.security.config.bean.JwtProperties;
import com.elitesland.security.config.bean.LoginProperties;
import com.elitesland.security.service.entity.JwtUserDto;
import com.elitesland.system.service.SysUserService;
import com.elitesland.system.vo.SysRoleVO;
import lombok.val;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <pre>
 * 用于Spring Security配置的，初始配置工作
 * </pre>
 *
 * @author Mir
 * @date 2020/6/15
 */
@Configuration
@EnableJpaAuditing
public class SecuritySetupConfig {

    @Bean
    @ConfigurationProperties(prefix = "login", ignoreUnknownFields = true)
    public LoginProperties captchaProperties() {
        return new LoginProperties();
    }

    @Bean
    @ConfigurationProperties(prefix = "jwt", ignoreUnknownFields = true)
    public JwtProperties securityProperties() {
        return new JwtProperties();
    }

    @Bean
    public RequestMatcherCreator requestMatcherCreator() {
        return metaResources -> metaResources.stream()
                .map(r -> new AntPathRequestMatcher(r.getT1(), r.getT2()))
                .collect(Collectors.toSet());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource() {
        return new DynamicFilterInvocationSecurityMetadataSource();
    }

    @Bean
    public RoleVoter roleVoter() {
        return new RoleVoter();
    }

    @Bean
    public AccessDecisionManager affirmativeBase(List<AccessDecisionVoter<?>> decisionVoters) {
        return new AffirmativeBased(decisionVoters);
    }

    @Bean
    public UserDetailsManager userDetailsManager() {
        return new UserDetailsManager() {
            @Resource
            private SysUserService sysUserService;

            @Resource
            private EmployeeService employeeService;

            @Override
            public void createUser(UserDetails user) {

            }

            @Override
            public void updateUser(UserDetails user) {

            }

            @Override
            public void deleteUser(String username) {

            }

            @Override
            public void changePassword(String oldPassword, String newPassword) {

            }

            @Override
            public boolean userExists(String username) {
                return sysUserService.isUserExists(username);
            }

            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                val sysUserOpt = sysUserService.getUserByUsernameSec(username);
                return sysUserOpt.map(u -> {
                    val employeeOpt = employeeService.oneBySysUserName(u.getUsername());

                    return new JwtUserDto(
                            u,
                            employeeOpt,
                            new ArrayList<Long>(),
                            new ArrayList<String>(),
                            AuthorityUtils.createAuthorityList(u.getRoles().stream().map(SysRoleVO::getCode).toArray(String[]::new))
                    );
                }).orElseThrow(new BusinessException("用户：" + username + "，未找到，请重试。"));
            }
        };
    }

    @Bean
    public AuditorAware<String> auditorProvider() {
        return new SpringSecurityAuditorAware();
    }
}
